<HTML>
 
<!-- Mirrored from www.javapractices.com/apps/movies/javadoc/src-html/hirondelle/movies/edit/MovieDAO.html by HTTrack Website Copier/3.x [XR&CO'2010], Sun, 12 Jun 2011 17:30:20 GMT -->
<HEAD>
  <TITLE>
MovieDAO.java
  </TITLE>
  <LINK REL ='stylesheet' TYPE='text/css' HREF='../../../../highlight.css' TITLE='Style'>
 </HEAD>
 <BODY>
<PRE>
<span class='keyword'>package</span> hirondelle.movies.edit;<a name=line.1></a>
<a name=line.2></a>
<span class='keyword'>import</span> java.util.*;<a name=line.3></a>
<span class='keyword'>import</span> java.io.*;<a name=line.4></a>
<span class='keyword'>import</span> java.util.regex.Pattern;<a name=line.5></a>
<span class='keyword'>import</span> java.util.logging.Logger;<a name=line.6></a>
<span class='keyword'>import</span> hirondelle.movies.util.Util;<a name=line.7></a>
<span class='keyword'>import</span> java.math.BigDecimal;<a name=line.8></a>
<a name=line.9></a>
<span class='keyword'>import</span> hirondelle.movies.exception.InvalidInputException;<a name=line.10></a>
<span class='keyword'>import</span> hirondelle.movies.main.MainWindow;<a name=line.11></a>
<a name=line.12></a>
<span class='comment'>/**<a name=line.13></a>
 Data Access Object (DAO) for {@link Movie} objects.<a name=line.14></a>
 <a name=line.15></a>
  &lt;P&gt; Implements persistence for movie information. This class uses a simple text file called<a name=line.16></a>
  &lt;tt&gt;movie_list_for_&lt;&lt;em&gt;user name&lt;/em&gt;&gt;.txt&lt;/tt&gt;, stored locally, in the application's<a name=line.17></a>
  home directory.<a name=line.18></a>
  Each logged in user gets their own list. Each logged in user can see their own list, <a name=line.19></a>
  but they cannot see anyone else's list.<a name=line.20></a>
  <a name=line.21></a>
  &lt;P&gt;The format of the file is specific to this application. The file should not be edited <a name=line.22></a>
  directly by an end user, in case the format is violated.<a name=line.23></a>
   <a name=line.24></a>
  &lt;P&gt;Upon startup, all records in the datastore are read into memory. Edits are performed initially<a name=line.25></a>
  only in memory. When the application shuts down, then the updated data store is written<a name=line.26></a>
  back to the disk, for use during the next launch of the application.<a name=line.27></a>
 */</span><a name=line.28></a>
<span class='keyword'>public</span> <span class='keyword'>final</span> <span class='keyword'>class</span> MovieDAO {<a name=line.29></a>
<a name=line.30></a>
  <span class='comment'>/**<a name=line.31></a>
    Save all data to a text file. Must be called explicitly when the<a name=line.32></a>
    app shuts down, in order to save all edits.<a name=line.33></a>
  */</span><a name=line.34></a>
  <span class='keyword'>public</span> <span class='keyword'>void</span> shutdown() {<a name=line.35></a>
    fLogger.fine(<span class='literal'>"Saving all miovie records to file."</span>);<a name=line.36></a>
    String fileContents = buildFileContents();<a name=line.37></a>
    writeStringToFile(fileContents);<a name=line.38></a>
  }<a name=line.39></a>
<a name=line.40></a>
  <span class='comment'>/** Add a new {@link Movie}. */</span><a name=line.41></a>
  <span class='keyword'>void</span> add(Movie aMovie) {<a name=line.42></a>
    String id = nextId();<a name=line.43></a>
    aMovie.setId(id.toString());<a name=line.44></a>
    fTable.put(id, aMovie);<a name=line.45></a>
  }<a name=line.46></a>
<a name=line.47></a>
  <span class='comment'>/** Change an existing {@link Movie}. */</span><a name=line.48></a>
  <span class='keyword'>void</span> change(Movie aMovie) {<a name=line.49></a>
    fTable.put(aMovie.getId(), aMovie);<a name=line.50></a>
  }<a name=line.51></a>
<a name=line.52></a>
  <span class='comment'>/**<a name=line.53></a>
   * List all {@link Movie}s. Order is the natural order of the {@link Movie} class<a name=line.54></a>
   * (descending date, then title).<a name=line.55></a>
   */</span><a name=line.56></a>
  List&lt;Movie&gt; list() {<a name=line.57></a>
    List&lt;Movie&gt; result = <span class='keyword'>new</span> ArrayList&lt;Movie&gt;(fTable.values());<a name=line.58></a>
    Collections.sort(result);<a name=line.59></a>
    <span class='keyword'>return</span> result;<a name=line.60></a>
  }<a name=line.61></a>
<a name=line.62></a>
  <span class='comment'>/** Delete an existing {@link Movie}, given the movie id. */</span><a name=line.63></a>
  <span class='keyword'>void</span> delete(String aMovieId) {<a name=line.64></a>
    fTable.remove(aMovieId);<a name=line.65></a>
  }<a name=line.66></a>
<a name=line.67></a>
  <span class='comment'>// PRIVATE //<a name=line.68></a>
</span>  <span class='keyword'>private</span> <span class='keyword'>static</span> <span class='keyword'>final</span> Map&lt;String, Movie&gt; fTable = <span class='keyword'>new</span> LinkedHashMap&lt;String, Movie&gt;();<a name=line.69></a>
  <span class='keyword'>private</span> <span class='keyword'>static</span> <span class='keyword'>int</span> fNextId = <span class='literal'>0</span>;<a name=line.70></a>
  <span class='keyword'>private</span> <span class='keyword'>static</span> <span class='keyword'>final</span> String MOVIES_FILE_NAME = <span class='literal'>"movie_list_for_"</span>;<a name=line.71></a>
  <span class='keyword'>private</span> <span class='keyword'>static</span> <span class='keyword'>final</span> String ENCODING = <span class='literal'>"UTF-8"</span>;<a name=line.72></a>
  <span class='keyword'>private</span> <span class='keyword'>static</span> <span class='keyword'>final</span> String DELIMITER = <span class='literal'>"|"</span>;<a name=line.73></a>
  <span class='keyword'>private</span> <span class='keyword'>static</span> <span class='keyword'>final</span> String NULL = <span class='literal'>"NULL"</span>;<a name=line.74></a>
  <span class='keyword'>private</span> <span class='keyword'>static</span> <span class='keyword'>final</span> Logger fLogger = Util.getLogger(MovieDAO.<span class='keyword'>class</span>);<a name=line.75></a>
<a name=line.76></a>
  <span class='keyword'>static</span> {<a name=line.77></a>
    readInMovieFileUponStartup();<a name=line.78></a>
    fLogger.config(<span class='literal'>"Number of movies read in from file: "</span> + fTable.size());<a name=line.79></a>
  }<a name=line.80></a>
<a name=line.81></a>
  <span class='keyword'>private</span> <span class='keyword'>static</span> <span class='keyword'>void</span> readInMovieFileUponStartup() {<a name=line.82></a>
    File file = <span class='keyword'>new</span> File(getMovieFileName());<a name=line.83></a>
    fLogger.fine(<span class='literal'>"Reading movies from file :"</span> + file.getAbsolutePath());<a name=line.84></a>
    String line = <span class='literal'>""</span>;<a name=line.85></a>
    Scanner scanner = <span class='keyword'>null</span>;<a name=line.86></a>
    <span class='keyword'>try</span> {<a name=line.87></a>
      scanner = <span class='keyword'>new</span> Scanner(file);<a name=line.88></a>
      <span class='keyword'>while</span> (scanner.hasNextLine()) {<a name=line.89></a>
        line = scanner.nextLine();<a name=line.90></a>
        <span class='keyword'>if</span> (Util.textHasContent(line)) {<a name=line.91></a>
          parseLine(line);<a name=line.92></a>
        }<a name=line.93></a>
      }<a name=line.94></a>
    }<a name=line.95></a>
    <span class='keyword'>catch</span> (FileNotFoundException ex) {<a name=line.96></a>
      fLogger.config(<span class='literal'>"Movies  file not present. Will be created when the app closes."</span>);<a name=line.97></a>
    }<a name=line.98></a>
    <span class='keyword'>catch</span> (InvalidInputException ex) {<a name=line.99></a>
      fLogger.severe(<span class='literal'>"Movies file: date-viewed field not in expected format: "</span> + line);<a name=line.100></a>
    }<a name=line.101></a>
    <span class='keyword'>catch</span> (InputMismatchException ex) {<a name=line.102></a>
      fLogger.severe(<span class='literal'>"Movies file: Not in expected format: "</span> + line);<a name=line.103></a>
    }<a name=line.104></a>
    <span class='keyword'>catch</span> (NoSuchElementException ex) {<a name=line.105></a>
      fLogger.severe(<span class='literal'>"Movies file: Not in expected format: "</span> + line);<a name=line.106></a>
    }<a name=line.107></a>
    <span class='keyword'>finally</span> {<a name=line.108></a>
      <span class='keyword'>if</span> (scanner != <span class='keyword'>null</span>) scanner.close();<a name=line.109></a>
    }<a name=line.110></a>
  }<a name=line.111></a>
<a name=line.112></a>
  <span class='keyword'>private</span> <span class='keyword'>static</span> <span class='keyword'>void</span> parseLine(String aLine) <span class='keyword'>throws</span> InvalidInputException {<a name=line.113></a>
    Scanner scanner = <span class='keyword'>new</span> Scanner(aLine);<a name=line.114></a>
    <span class='comment'>// note how the quoting is needed here, since '|' is a special character in<a name=line.115></a>
</span>    <span class='comment'>// regular expressions :<a name=line.116></a>
</span>    scanner.useDelimiter(Pattern.quote(DELIMITER));<a name=line.117></a>
    scanner.useLocale(Locale.US);<a name=line.118></a>
    <span class='keyword'>if</span> (scanner.hasNext()) {<a name=line.119></a>
      String title = scanner.next();<a name=line.120></a>
      Date viewed = Util.parseDate(maybeNull(scanner.next()), <span class='literal'>"Date Viewed"</span>);<a name=line.121></a>
      BigDecimal rating = Util.parseBigDecimal(maybeNull(scanner.next()), <span class='literal'>"Rating"</span>);<a name=line.122></a>
      String comment = maybeNull(scanner.next());<a name=line.123></a>
      Movie movie = <span class='keyword'>new</span> Movie(nextId().toString(), title, viewed, rating, comment);<a name=line.124></a>
      fTable.put(movie.getId(), movie);<a name=line.125></a>
    }<a name=line.126></a>
    scanner.close();<a name=line.127></a>
  }<a name=line.128></a>
<a name=line.129></a>
  <span class='keyword'>private</span> <span class='keyword'>static</span> String nextId() {<a name=line.130></a>
    ++fNextId;<a name=line.131></a>
    <span class='keyword'>return</span> String.valueOf(fNextId);<a name=line.132></a>
  }<a name=line.133></a>
<a name=line.134></a>
  <span class='keyword'>private</span> <span class='keyword'>void</span> appendTo(StringBuilder aText, Object aField, String aAppend) {<a name=line.135></a>
    <span class='keyword'>if</span> (Util.textHasContent(Util.format(aField))) {<a name=line.136></a>
      aText.append(Util.format(aField));<a name=line.137></a>
    }<a name=line.138></a>
    <span class='keyword'>else</span> {<a name=line.139></a>
      aText.append(NULL);<a name=line.140></a>
    }<a name=line.141></a>
    aText.append(aAppend);<a name=line.142></a>
  }<a name=line.143></a>
<a name=line.144></a>
  <span class='keyword'>private</span> <span class='keyword'>static</span> String maybeNull(String aText) {<a name=line.145></a>
    <span class='keyword'>return</span> NULL.equals(aText) ? <span class='keyword'>null</span> : aText;<a name=line.146></a>
  }<a name=line.147></a>
<a name=line.148></a>
  <span class='keyword'>private</span> <span class='keyword'>static</span> String getMovieFileName() {<a name=line.149></a>
    <span class='keyword'>return</span> MOVIES_FILE_NAME + MainWindow.getInstance().getUserName().toLowerCase(Locale.ENGLISH) + <span class='literal'>".txt"</span>;<a name=line.150></a>
  }<a name=line.151></a>
<a name=line.152></a>
  <span class='comment'>/** Create a string, holding all movie records. */</span><a name=line.153></a>
  <span class='keyword'>private</span> String buildFileContents() {<a name=line.154></a>
    String NEW_LINE = System.getProperty(<span class='literal'>"line.separator"</span>);<a name=line.155></a>
    StringBuilder result = <span class='keyword'>new</span> StringBuilder();<a name=line.156></a>
    <span class='keyword'>for</span> (Movie movie : fTable.values()) {<a name=line.157></a>
      appendTo(result, movie.getTitle(), DELIMITER);<a name=line.158></a>
      appendTo(result, movie.getDateViewed(), DELIMITER);<a name=line.159></a>
      appendTo(result, movie.getRating(), DELIMITER);<a name=line.160></a>
      appendTo(result, movie.getComment(), NEW_LINE);<a name=line.161></a>
    }<a name=line.162></a>
    <span class='keyword'>return</span> result.toString();<a name=line.163></a>
  }<a name=line.164></a>
<a name=line.165></a>
  <span class='comment'>/** Write string containing all movie records to a file - overwrite the whole file. */</span><a name=line.166></a>
  <span class='keyword'>private</span> <span class='keyword'>void</span> writeStringToFile(String aFileContents) {<a name=line.167></a>
    Writer output = <span class='keyword'>null</span>;<a name=line.168></a>
    <span class='keyword'>try</span> {<a name=line.169></a>
      FileOutputStream fos = <span class='keyword'>new</span> FileOutputStream(getMovieFileName());<a name=line.170></a>
      OutputStreamWriter out = <span class='keyword'>new</span> OutputStreamWriter(fos, ENCODING);<a name=line.171></a>
      output = <span class='keyword'>new</span> BufferedWriter(out);<a name=line.172></a>
      output.write(aFileContents);<a name=line.173></a>
    }<a name=line.174></a>
    <span class='keyword'>catch</span> (FileNotFoundException ex) {<a name=line.175></a>
      fLogger.severe(<span class='literal'>"Cannot find movies.txt file."</span>);<a name=line.176></a>
    }<a name=line.177></a>
    <span class='keyword'>catch</span> (UnsupportedEncodingException ex) {<a name=line.178></a>
      fLogger.severe(<span class='literal'>"System does not support UTF-8 encoding."</span>);<a name=line.179></a>
    }<a name=line.180></a>
    <span class='keyword'>catch</span> (IOException ex) {<a name=line.181></a>
      fLogger.severe(<span class='literal'>"Problem while saving movies.txt file."</span>);<a name=line.182></a>
    }<a name=line.183></a>
    <span class='keyword'>finally</span> {<a name=line.184></a>
      <span class='keyword'>if</span> (output != <span class='keyword'>null</span>) {<a name=line.185></a>
        <span class='keyword'>try</span> {<a name=line.186></a>
          output.close();<a name=line.187></a>
        }<a name=line.188></a>
        <span class='keyword'>catch</span> (IOException ex) {<a name=line.189></a>
          fLogger.severe(<span class='literal'>"Cannot close stream."</span>);<a name=line.190></a>
        }<a name=line.191></a>
      }<a name=line.192></a>
    }<a name=line.193></a>
  }<a name=line.194></a>
}<a name=line.195></a>
 <a name=line.196></a>
</PRE><a name=line.197></a>
 </BODY><a name=line.198></a>
<HTML><a name=line.199></a>
